package com.brightness.security.oauth2.server.config;

import com.brightness.security.oauth2.server.config.service.UserDetailsServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: mailtohgh@163.com
 * @date: 2019/12/30
 * @time: 3:42 下午
 * @descript:
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,jsr250Enabled = true,securedEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService(){
        return new UserDetailsServiceImpl();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/oauth/check_token");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
                .csrf().disable()
                .formLogin()
                .loginProcessingUrl("/api/login/account")
                .usernameParameter("userName")
                .passwordParameter("password")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setStatus(200);
                        response.setHeader("Content-Type", "application/json;charset=utf-8");
                        PrintWriter writer = response.getWriter();
                        ObjectMapper mapper = new ObjectMapper();
                        writer.write(mapper.writeValueAsString(LoginResultDto.success(authentication)));
                        writer.flush();
                        writer.close();
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.setStatus(200);
                        response.setHeader("Content-Type", "application/json;charset=utf-8");
                        PrintWriter writer = response.getWriter();
                        ObjectMapper mapper = new ObjectMapper();
                        writer.write(mapper.writeValueAsString(LoginResultDto.fail()));
                        writer.flush();
                        writer.close();
                    }
                });
    }

    private static class LoginResultDto{

        private String status = "ok";
        private String type = "account";

        private List<String> currentAuthority = new ArrayList<>();

        public static LoginResultDto success(Authentication authentication){
            return new LoginResultDto(authentication);
        }

        public static LoginResultDto fail(){
            LoginResultDto loginResultDto = new LoginResultDto();
            loginResultDto.setStatus("error");
            loginResultDto.getCurrentAuthority().add("guest");
            return loginResultDto;
        }

        private LoginResultDto(){}

        private LoginResultDto(Authentication authentication) {
            authentication.getAuthorities().forEach(auth->{
                currentAuthority.add(auth.getAuthority());
            });
        }

        public List<String> getCurrentAuthority() {
            return currentAuthority;
        }

        public String getStatus() {
            return status;
        }

        public String getType() {
            return type;
        }

        public void setStatus(String status) {
            this.status = status;
        }

        public void setCurrentAuthority(List<String> currentAuthority) {
            this.currentAuthority = currentAuthority;
        }
    }
}